	.386p

_TEXT	segment byte public 'CODE' use16
_TEXT	ends

DGROUP	group	_DATA,_BSS
	assume	cs:_TEXT,ds:DGROUP

_DATA	segment word public 'DATA' use16
	SaveSP	dw	0
	SaveSS	dw	0
_DATA	ends

_BSS	segment word public 'BSS' use16
_BSS	ends

_TEXT	segment byte public 'CODE' use16
	assume	cs:_TEXT,ds:DGROUP

	; unsigned long get_total_memory(void)
	; asm mov	ax,0x0e801
	; asm	int	0x15
	; asm mov __edx, edx
	; total_memory = ((__edx & 0xFFFF) << 6) + (unsigned long)(_CX);

	_get_total_memory proc	near
	
		mov	ax, 0e801h
		int	015h
		and edx, 0ffffh
		shl edx, 6
		add edx, ecx
		mov ax, dx
		shr edx, 16
		ret
	
	_get_total_memory endp

	; void CopyMemory(DWORD Dest, void *src, int bytes)
	; [bp + 4] = Dest
	; [bp + 8] = src
	; [bp + 10] = bytes

	_CopyMemory	proc	near

		push	bp
		mov	bp,sp

		push	bx
		push	si
		push 	cx
		push	eax
		mov	cx,  word ptr [bp + 10]
		mov	eax, dword ptr [bp + 4]
		mov	si,  word ptr [bp + 8]

	_@loop:
		mov	bl, [si]
		mov	fs:[eax], bl
		inc	si
		inc	eax
		loop	short _@loop

		pop	eax
		pop	cx
		pop	si
		pop	bx
		pop	bp
		ret

	_CopyMemory	endp

	;
	; WORD enter_protect_mode(
	;	uint LinearAddress
	;	)
	;
	; Enter the protect mode, and jump to
	; the protect code.
	; At last, return the real mode,
	; and return the value.
	;

	_enter_protect_mode proc	near

	;
	; Save all the register except AX
	; sp will be save before, so skip it
	;
		cli
		push	bp
		mov	bp, sp
	;
	; [bp + 4]  = LinearAddress
	;

		pushf
		push	bx
		push	cx
		push	dx
		push	si
		push	di
		push	es

		;
		; (1) Set the base of 16bit segment descriptor
		;

		xor	eax, eax
		mov	ax, _TEXT
		shl	eax, 4
		mov	bx, offset _GDT_Table + 32
		mov	[bx + 2], ax
		shr	eax, 16
		mov	[bx + 4], al
		mov	[bx + 7], ah 

		;
		; (2) Save IDTR
		;
		sub	sp, 6
		sidt	qword ptr ss:[esp]

		mov	SaveSP, sp
		mov	ax, ss
		mov	SaveSS, ax

		;
		; (3) Save Return Address
		;

		mov	eax, cs
		shl	eax, 4
		xor	ebx, ebx
		mov	bx, offset RealMode
		add	eax, ebx
		push	eax

		;
		; (4) Save three parameters
		;

		; not necessary, removed

		;
		; (5) Set the Protect Mode entry
		;

		mov	eax, dword ptr [bp + 4]
		sub eax, 0C0000000h
		mov	si,  offset LinearAddress
		mov	cs:[si], eax

		;
		; (6) Set the ESP as its linear address
		; NOTE: After this step, we cannot use stack any more

		mov	eax, ss
		shl	eax, 4
		xor	ebx, ebx
		mov	bx, sp
		add	eax, ebx
		mov	esp, eax

		;
		; (7) Enter to the protect mode
		;

		mov	ecx, ds
		shl	ecx, 4
		mov	word ptr _pdescr_tmp [0],(5*8-1)

		xor	eax, eax
		mov	ax,  offset _GDT_Table
		add	eax, ecx
		mov	word ptr _pdescr_tmp [2], ax
		shr	eax, 16
		mov	word ptr _pdescr_tmp [4], ax
		lgdt	qword ptr _pdescr_tmp

		mov	edx, 012345678H		; Magic

		mov	eax, cr0
		or		ax,  1
		mov	cr0, eax

		db		066H
		db		0EAH
		LinearAddress label dword
		dd		00000000H
		dw		0008H

	RealMode:

		;
		; return 16-bit protect mode
		;

		db	0B9H, 018H, 0, 0, 0						;mov ecx, 0x18
		db	08EH, 0D9H								;mov ds, ecx
		db 08EH, 0C1H								;mov es, ecx
		db 08EH, 0E1H								;mov fs, ecx
		db 08EH, 0E9H								;mov gs, eax
		db 08EH, 0D1H								;mov ss, ecx

		db	0EAH
		dw	offset Protect16
		dw	0000H
		dw 020H

	Protect16:
		mov	ebx, eax

		mov	eax, cr0
		and	eax, 0FFFFFFFEH
		mov	cr0, eax

		db	0EAH
		dw	offset RealNext
		dw	seg	 RealNext

	RealNext:

		;
		; restore ds, and ss
		;

		xor	esp, esp
		mov	ax, _DATA
		mov	ds, ax

		lss	sp, dword ptr SaveSP

		;
		; Restore IDTR for DOS
		;
		lidt	qword ptr ss:[esp]
		add	sp, 6

		mov	eax, ebx

		xor	edi, edi
		xor	esi, esi
		xor	edx, edx
		xor	ecx, ecx
		xor	ebx, ebx
		xor	ebp, ebp

		pop	es
		pop	di
		pop	si
		pop	dx
		pop	cx
		pop	bx

		popf

		pop	bp
		sti
		ret

	_enter_protect_mode endp

_TEXT	ends

	extrn		_pdescr_tmp
	extrn		_GDT_Table

	public	_CopyMemory
	public	_get_total_memory
	public	_enter_protect_mode

	end
